﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;

using UnityEngine;

// 推棒法
public class MazeBuilder2 : MonoBehaviour {

    [SerializeField]
    private GameObject ground;
    [SerializeField]
    private GameObject wallPrefab;
    [SerializeField]
    private Camera mainCamera;

    private int mazeHeight;
    private int mazeWidth;
    private bool [, ] maze;
    private List<Vector2> searchDirectionsFirst = new List<Vector2> { Vector2.up, Vector2.down, Vector2.right, Vector2.left };
    private List<Vector2> searchDirections = new List<Vector2> { Vector2.up, Vector2.right, Vector2.left };

    private void Start ( ) {
        CreateMaze (21, 21);
    }

    private void CreateMaze (int height, int width) {
        mazeWidth = width;
        mazeHeight = height;

        InitializeMaze ( );

        StartCoroutine (CreateMaze ( ));
    }

    private void InitializeMaze ( ) {
        maze = new bool [mazeWidth, mazeHeight];

        var centered = new Vector3 (mazeWidth / 2f - 0.5f, 0, mazeHeight / 2f - 0.5f);
        ground.transform.localScale = new Vector3 (mazeWidth, 1, mazeHeight);
        ground.transform.position = centered + Vector3.down * 0.5f;

        mainCamera.transform.position = centered + Vector3.up * ((mazeWidth + mazeHeight) / 2f);
    }

    private IEnumerator CreateMaze ( ) {
        var startPositions = new List<Vector2> ( );
        for (int i = 0; i < mazeWidth; i++) {
            for (int j = 0; j < mazeWidth; j++) {
                if (i == 0 || j == 0 || i == mazeWidth - 1 || j == mazeWidth - 1) {
                    Instantiate (wallPrefab, new Vector3 (i, 0.5f, j), Quaternion.identity);
                } else if (i % 2 == 0 && j % 2 == 0) {
                    Instantiate (wallPrefab, new Vector3 (i, 0.5f, j), Quaternion.identity);
                    startPositions.Add (new Vector2 (i, j));
                }
            }
        }

        foreach (var pos in startPositions) {
            if (pos.x == 2) {
                foreach (var dir in searchDirectionsFirst.OrderBy (i => Guid.NewGuid ( ))) {
                    var checkPos = pos + dir;
                    if (maze [(int) checkPos.x, (int) checkPos.y]) {
                        continue;
                    } else {
                        yield return CreateWall (checkPos);
                        break;
                    }
                }
            } else {
                foreach (var dir in searchDirections.OrderBy (i => Guid.NewGuid ( ))) {
                    var checkPos = pos + dir;
                    if (maze [(int) checkPos.x, (int) checkPos.y]) {
                        continue;
                    } else {
                        yield return CreateWall (checkPos);
                        break;
                    }
                }
            }
        }

        yield break;
    }

    private IEnumerator CreateWall (Vector2 position) {
        maze [(int) position.x, (int) position.y] = true;
        Instantiate (wallPrefab, new Vector3 (position.x, 0.5f, position.y), Quaternion.identity);
        yield return new WaitForSeconds (0.05f);
    }
}
